
package combat.dudes.operator;

import gnu.cajo.utils.extra.TransparentItemProxy;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import combat.dudes.DudesConfig;
import combat.dudes.DudesFunctions;
import combat.dudes.operator.Core.commandList;
import combat.dudes.protocol.data.DoublePoint;
import combat.dudes.protocol.data.PhysicalObject;
import combat.dudes.protocol.interfaces.OnboardSensorSystemInt;
import combat.dudes.protocol.interfaces.OnboardSystemInt;
import combat.dudes.protocol.interfaces.OnboardWeaponSystemInt;
import combat.dudes.protocol.interfaces.OrderAttackInt;
import combat.dudes.protocol.interfaces.OrderInt;
import combat.dudes.protocol.interfaces.TeamInt;
import combat.dudes.protocol.interfaces.UserInt;
import combat.dudes.protocol.interfaces.VehicleInt;
import combat.dudes.protocol.types.VehicleType;
import combat.dudes.server.protocol.implementation.Vehicle;


/**
 * @author Erik Meijer
 */

public class OperatorCommands {

	static BufferedReader OperatorInput = new BufferedReader(new InputStreamReader(System.in));

	static String[] tokens = new String[] {};
	static String input = new String();
	static String mistake = null;

	static boolean valid = true;
	static double simspeed;

	static TeamInt targetTeam;
	static UserInt targetUser;
	static VehicleInt targetVehicle;


	static String handleInput() throws Exception {
		input = OperatorInput.readLine().toString();

		if(OperatorApp.isConnected) {
			tokens = input.split(" ");

			Core.makeTeamList();
			Core.makeUserList();
			Core.makeVehicleList();

			commandHandler(Core.commandChecker(tokens[0]));
		}
		return input;
	}

	static void commandHandler(commandList input) throws Exception {
		switch(input) {
			case UNKNOWN:
				System.out.println("Unknown command, please type \"help\" for a list of available commands.");
				break;
			case EXIT:
				OperatorApp.isRunning = false;
				System.out.println("OperatorApp stopping...");
				break;
			case HELP:
				printHelpMessage();
				break;
			case TEAMS:
				printTeams();
				break;
			case VEHICLES:
				printVehicles();
				break;
			case START:
				Core.sim.startSimulation();
				System.out.println("Simulation has started.");
				break;
			case STOP:
				Core.sim.stopSimulation();
				System.out.println("Simulation is now stopped.");
				break;
			case PAUSE:
				simspeed = Core.sim.getSimSpeed();
				Core.sim.setSimSpeed(0.0);
				System.out.println("Simulation is now paused.");
				break;
			case RESUME:
				Core.sim.setSimSpeed(simspeed);
				System.out.println("Simulation resumed, simspeed is set back to: " + Core.sim.getSimSpeed().toString());
				break;
			case SIMSPEED:
				if(Core.tokenChecker(2, tokens) && Core.isDouble(tokens[1]) && Core.doubleParser(tokens[1], tokens) >= 0 && Core.doubleParser(tokens[1], tokens) <= 500) {
					Core.sim.setSimSpeed(Core.doubleParser(tokens[1], tokens));
					System.out.println("New simspeed: " + Core.sim.getSimSpeed().toString());
				} else System.out.println("Invalid input for the set simspeed command.");
				break;
			case TEAMSTATUS:
				if(Core.tokenChecker(2, tokens) && Core.isInt(tokens[1])) {
					printTeamStatus(Integer.parseInt(tokens[1]));
				} else System.out.println("Invalid parameters for the teamstatus command.");
				break;
			case USERSTATUS:
				if(Core.tokenChecker(2, tokens) && Core.isInt(tokens[1])) {
					printUserStatus(Integer.parseInt(tokens[1]));
				} else System.out.println("Invalid parameters for the userstatus command.");
				break;
			case VEHICLESTATUS:
				if(Core.tokenChecker(3, tokens) && Core.isInt(tokens[1]) && Core.isInt(tokens[2])) {
					printVehicleStatus(Integer.parseInt(tokens[1]), Integer.parseInt(tokens[2]));
				} else System.out.println("Invalid parameters for the vehiclestatus command.");
				break;
			case MOVEUSER:
				if(Core.tokenChecker(3, tokens) && Core.isInt(tokens[1]) && Core.isInt(tokens[2])) {
					moveUser(Integer.parseInt(tokens[1]), Integer.parseInt(tokens[2]));
					printTeams();
				} else System.out.println("Invalid parameters for the moveuser command.");
				break;
			case MAKEVEHICLE:
				createVehicleWiz();
				printVehicles();
				break;
			case ASSIGNVEHICLE:
				assignVehicleWiz();
				printTeams();
				break;
			case QUICKVEHICLE:
				if(Core.tokenChecker(6, tokens) && Core.isInt(tokens[1]) && Core.isInt(tokens[2])) {
					quickVehicle(Integer.parseInt(tokens[1]), Integer.parseInt(tokens[2]), tokens[3], tokens[4], tokens[5]);
					printTeams();
				} else System.out.println("Invalid parameters for the quickvehicle command.");
				break;
			case QUICKASSIGN:
				if(Core.tokenChecker(3, tokens) && Core.isInt(tokens[1])) {
					quickAssign(Integer.parseInt(tokens[1]), tokens[2]);
					printTeams();
				} else System.out.println("Invalid parameters for the quickassign command.");
				break;
			case CLEARVEHICLE:
				if(Core.tokenChecker(3, tokens) && Core.isInt(tokens[1]) && Core.isInt(tokens[2])) {
					clearvehicle(Integer.parseInt(tokens[1]), Integer.parseInt(tokens[2]));
					printTeams();
				} else System.out.println("Invalid parameters for the clearvehicle command.");
				break;
			case LOCKVEHICLE:
				if(Core.tokenChecker(4, tokens) && Core.isInt(tokens[1]) && Core.isInt(tokens[2])) {
					lockVehicle(Integer.parseInt(tokens[1]), Integer.parseInt(tokens[2]), tokens[3]);
				} else System.out.println("Invalid parameters for the lockvehicle command.");
				break;
		}
	}

	static void printHelpMessage() {
		System.out.println("exit - stops the operator console.");
		System.out.println("start - starts the simulator with the current configuration");
		System.out.println("stop - ends the simulation");
		System.out.println("pause - stops the simulation until either the stop or resume command is issued");
		System.out.println("resume - resumes the simulation if it was previously paused");
		System.out.println("simspeed - sets the simulation speed, takes 1 parameter that has to be a number.");
		System.out.println("teamstatus - lists all user occupied vehicles and their location of the specified team, takes 1 parameter: team ID");
		System.out.println("userstatus - lists the specified user's vehicle data. Also attempts to print an orderlist of this user's vehicle, takes 1 parameter: user ID.");
		System.out.println("vehiclestatus - lists the specified vehicle's data and weapon system data, takes 2 parameters: team ID and vehicle ID.");
		System.out.println("teams - displays the current condition of the teams. Lists each team's users, specificly their user ID's, user names and vehicles");
		System.out.println("vehicles - displays the current condition of the vehicles. Lists each team's vehicles, specificly their types and names");
		System.out.println("moveuser - moves specified user to specified team, takes 2 parameters: user id and team id.");
		System.out.println("makevehicle - initiates a wizard that allows you to create a  new vehicle, vehicle attributes with default values do not require to be set when prompted.");
		System.out.println("assignvehicle - initiates a wizard that allows you to assign a vehicle to a user, if they are both in the same team.");
		System.out.println("quickvehicle - makes a vehicle in 1 input line, takes 5 paramaters: user id, team id, vehicle type, starting x position and starting y position.");
		System.out.println("quickassign - assigns a vehicle in 1 input line, takes 2 parameters: user id and vehicle name.");
		System.out.println("clearvehicle - boots whoever out of the specified vehicle, takes 2 parameters: team id and vehicle id.");
		System.out.println("lockvehicle - locks or unlocks specified vehicle, takes 3 parameters: team ID, vehicle ID and either of the following states: true or false.");
	}

	static void printTeams() {
		for(TeamInt team : Core.teams) {
			System.out.println(team.getTeamID() + " " + team.getTeamName() + ": " + "players: " + team.getUsers().size());
			for(UserInt user : team.getUsers()) {
				String userVehicle = "none";
				if(user.getUserVehicle() != null)
					userVehicle = user.getUserVehicle().getName();
				System.out.println("  " + user.getUserID() + "  " + user.getUserName() + "  " + userVehicle);
			}
		}
	}

	static void printVehicles() {
		for(TeamInt team : Core.teams) {
			System.out.println(team.getTeamID() + " " + team.getTeamName() + ", " + "vehicles: " + team.getVehicles().size());
			int i = 0;
			for(VehicleInt vehicle : team.getVehicles()) {
				System.out.println("  " + i + "  " + vehicle.getVehicleType().toString() + "  " + vehicle.getName());
				i++;
			}
		}
	}

	static void printTeamStatus(int teamID) {

		mistake = null;

		if(Core.teamChecker(teamID) != null)
			targetTeam = Core.teamChecker(teamID);
		else mistake = ", team not found";

		if(mistake == null)
			if(targetTeam.getUsers().size() >= 1) {
				System.out.println("Team " + targetTeam.getTeamName() + " status:");
				for(UserInt user : targetTeam.getUsers())
					if(user.getUserVehicle() != null)
						System.out.println("  " + user.getUserVehicle().getVehicleType().toString() + "  " + user.getUserVehicle().getName() + "  " + user.getUserVehicle().getPosition().toString()
								+ ".");
			} else System.out.println("Team " + targetTeam.getTeamName() + " has no occupied vehicles.");
		else System.out.println("Printing team status failed" + mistake);
	}

	static void printUserStatus(int userID) {

		mistake = null;

		if(Core.userChecker(userID) != null)
			targetUser = Core.userChecker(userID);
		else mistake = ", user not found";

		if(targetUser.getUserVehicle() == null)
			mistake = ", user has no vehicle yet";

		if(mistake == null) {
			System.out.println(targetUser.getUserName() + "  " + targetUser.getUserVehicle().getVehicleType().toString() + "  "
					+ DudesFunctions.DoublePointToString(targetUser.getUserVehicle().getPosition()) + ".");

			String orderDetails = new String();

			if(targetUser.getUserVehicle().getOrders() != null)
				for(OrderInt order : targetUser.getUserVehicle().getOrders()) {
					switch(order.getType()) {
						case MOV:
							orderDetails = DudesFunctions.DoublePointToString((DoublePoint) order.getTarget());
							break;
						case ATK:
							orderDetails = " " + ((OrderAttackInt) order).getSystem().getWeaponSystemType().toString();
							if(order.getTarget().getClass().getSuperclass().equals(PhysicalObject.class)) {
								PhysicalObject physobj = (PhysicalObject) order.getTarget();
								orderDetails += (" " + DudesFunctions.DoublePointToString(physobj.getPosition()));
							} else orderDetails += (" " + DudesFunctions.DoublePointToString((DoublePoint) order.getTarget()));
							break;
						case UNKNOWN:
							orderDetails = " unknown order.";
							break;
					}
					System.out.println(" " + order.getOrderID() + " " + order.getType().toString() + orderDetails + ".");
				}
			else System.out.println("User has no active orders.");
		} else System.out.println("Printing user status failed" + mistake + ".");
	}

	static void printVehicleStatus(int teamID, int vehicleID) {

		mistake = null;

		if(Core.teamChecker(teamID) != null)
			targetTeam = Core.teamChecker(teamID);
		else mistake = ", user not found.";
		if(Core.vehicleChecker(teamID, vehicleID) != null)
			targetVehicle = Core.vehicleChecker(teamID, vehicleID);
		else mistake = ", vehicle not found";

		if(mistake == null) {
			System.out.println(targetVehicle.getVehicleType() + " " + targetVehicle.getName() + "\nhealth: " + targetVehicle.getHealth() + "\nfuel: " + targetVehicle.getFuel() + "\nposition: "
					+ targetVehicle.getPosition() + "\ndirection: " + targetVehicle.getDirection());
			for(OnboardSystemInt system : targetVehicle.getOnboardSystems()) {
				switch(system.getOnboardSystemType()) {
					case WEAPON:
						System.out.println(((OnboardWeaponSystemInt) system).getWeaponSystemType().toString() + " " + ((OnboardWeaponSystemInt) system).getAmmo() + " "
								+ ((OnboardWeaponSystemInt) system).getOnboardSystemStatus().toString());
						break;
					case SENSOR:
						System.out.println(((OnboardSensorSystemInt) system).getSensorSystemType().toString() + " " + ((OnboardSensorSystemInt) system).getOnboardSystemStatus().toString());
						break;
					default:
						break;
				}
			}
		} else System.out.println("Printing vehicle status failed" + mistake);
	}

	static void moveUser(int userID, int teamID) {

		mistake = null;

		if(Core.userChecker(userID) != null)
			targetUser = Core.userChecker(userID);
		else mistake = " User not found";

		if(Core.teamChecker(teamID) != null)
			targetTeam = Core.teamChecker(teamID);
		else mistake = " Team not found";

		if(mistake == null)
			Core.sim.switchTeam_Remote(targetUser, targetTeam);
		else System.out.println("Moving user failed," + mistake + ".");
	}

	static void createVehicleWiz() throws Exception {
		int teamID = 0;
		String name = null;
		VehicleType vehicleType = null;
		double startposx = 0;
		double startposy = 0;
		double speed = 0;
		double fuel = 0;
		int health = 100;
		double direction = 0;
		DoublePoint position;

		boolean valid = false;
		while(!valid) {
			System.out.println("Please enter vehicle name..");
			if(Core.inputChecker(input)) {
				name = OperatorInput.readLine().toString();
				valid = true;
			}
			if(!valid)
				System.out.println("Invalid input, vehicle name should be a String.");
		}

		valid = false;
		while(!valid) {
			System.out.println("Please enter the vehicle's teamID..");
			input = (OperatorInput.readLine().toString());
			if(Core.isInt(input)) {
				teamID = Integer.parseInt(input);
				if(Core.teamChecker(teamID) != null) {
					targetTeam = Core.teamChecker(teamID);
					valid = true;
				} else System.out.println("Team not found, please try again.");
			} else System.out.println("Invalid input, teamID should be an int value.");
		}

		valid = false;
		while(!valid) {
			System.out.println("Please enter vehicle type..");
			input = OperatorInput.readLine().toString().toUpperCase();
			if(Core.inputChecker(input))
				if(Core.vehicleTypeChecker(input) != null) {
					vehicleType = Core.vehicleTypeChecker(input);
					valid = true;
				} else System.out.println("Vehicletype not found, valid inputs are frigate, submarine or convoy.");
			else System.out.println("Invalid input, vehicle type should be a string.");
		}

		valid = false;
		while(!valid) {
			System.out.println("Please enter startposition x value..");
			input = OperatorInput.readLine().toString();
			if(Core.isDouble(input)) {
				startposx = Double.parseDouble(input);
				valid = true;
			} else System.out.println("Invalid input, double expected.");
		}

		valid = false;
		while(!valid) {
			System.out.println("Please enter startposition y value");
			input = OperatorInput.readLine().toString();
			if(Core.isDouble(input)) {
				startposy = Double.parseDouble(input);
				valid = true;
			} else System.out.println("Invalid input, double expected.");
		}

		valid = false;
		while(!valid) {
			System.out.println("Please enter speed, default is " + DudesConfig.SharedVars.getMaxSpeed(vehicleType) + "..");
			input = (OperatorInput.readLine().toString());
			if(!Core.inputChecker(input)) {
				speed = DudesConfig.SharedVars.getMaxSpeed(vehicleType);
				valid = true;
			} else if(Core.isDouble(input) && Double.parseDouble(input) <= DudesConfig.SharedVars.getMaxSpeed(vehicleType)) {
				speed = Double.parseDouble(input);
				valid = true;
			} else System.out.println("Invalid input, double expected.");
		}

		valid = false;
		while(!valid) {
			System.out.println("Please enter fuel, default is " + DudesConfig.SharedVars.getMaxFuel(vehicleType) + "..");
			input = (OperatorInput.readLine().toString());
			if(!Core.inputChecker(input)) {
				fuel = DudesConfig.SharedVars.getMaxFuel(vehicleType);
				valid = true;
			} else if(Core.isDouble(input) && Double.parseDouble(input) <= DudesConfig.SharedVars.getMaxFuel(vehicleType)) {
				fuel = Double.parseDouble(input);
				valid = true;
			} else System.out.println("Invalid input, double expected.");
		}

		valid = false;
		while(!valid) {
			System.out.println("Please enter health, default is 100..");
			input = (OperatorInput.readLine().toString());
			if(!Core.inputChecker(input)) {
				health = 100;
				valid = true;
			} else if(Core.isInt(input) && Integer.parseInt(input) <= 100) {
				health = Integer.parseInt(input);
				valid = true;
			} else System.out.println("Invalid input, health should be an int value.");
		}

		valid = false;
		while(!valid) {
			System.out.println("Please enter direction, default is 0..");
			input = OperatorInput.readLine().toString();
			if(!Core.inputChecker(input)) {
				direction = 0;
				valid = true;
			} else if(Core.isDouble(input)) {
				direction = Double.parseDouble(input);
				valid = true;
			} else System.out.println("Invalid input, double expected.");
		}

		position = new DoublePoint(startposx, startposy);

		Core.teamChecker(teamID).addVehicle((VehicleInt) new Vehicle(vehicleType, name, position, 0, direction, speed, fuel, health, false, null));
	}

	static void assignVehicleWiz() throws Exception {
		boolean valid = false;
		int teamID = 0;
		int userID = 0;
		int vehicleID = 0;

		// obtaining target team of the vehicle
		valid = false;
		while(!valid) {
			System.out.println("Please enter the vehicle's teamID..");
			input = OperatorInput.readLine().toString();
			if(Core.isInt(input)) {
				teamID = Integer.parseInt(input);
				if(Core.teamChecker(teamID) != null) {
					targetTeam = Core.teamChecker(teamID);
					valid = true;
				} else System.out.println("Team not found, please try again.");
			} else System.out.println("Invalid input, teamID should be an int value.");
		}

		// obtaining user's ID to determine which user the vehicle goes to
		valid = false;
		while(!valid) {
			System.out.println("Please enter userID..");
			input = OperatorInput.readLine().toString();
			if(Core.isInt(input)) {
				userID = Integer.parseInt(input);
				if(Core.userChecker(userID) != null) {
					targetUser = Core.userChecker(userID);
					valid = true;
				} else System.out.println("User not found, please try again.");
			}
			if(!valid)
				System.out.println("Invalid input, userID should be an int value.");
		}

		// obtaining vehicle's ID to determine which vehicle will be assigned
		valid = false;
		while(!valid) {
			System.out.println("Please enter vehicle's ID.");
			input = OperatorInput.readLine().toString();
			if(Core.isInt(input)) {
				vehicleID = Integer.parseInt(input);
				if(Core.vehicleChecker(teamID, vehicleID) != null) {
					targetVehicle = Core.vehicleChecker(teamID, vehicleID);
					valid = true;
				} else System.out.println("Vehicle not found, please try again.");
			} else System.out.println("Invalid input, vehicleID should be an int value.");
		}

		// if all 3 inputs were valid, user is in the specified team and the vehicle is not locked, assign vehicle accordingly and lock it.
		if(valid && Core.userTeamFinder(userID).equals(targetTeam) && !targetVehicle.getLocked()) {
			// if user had a vehicle already, unlock it.
			if(targetUser.getUserVehicle() != null)
				targetUser.getUserVehicle().setLocked(false);
			targetUser.setUserVehicle(targetVehicle);
			targetVehicle.setLocked(true);
		} else System.out.println("Vehicle assign failed.. \nSpecified user was in team " + Core.userTeamFinder(userID).getTeamName() +
					". \nSpecified team was " + targetTeam.getTeamName() + ". \nVehicle locked was " + targetVehicle.getLocked() + ". \n");
	}

	static void quickVehicle(int userID, int teamID, String type, String x, String y) throws Exception {
		VehicleType vehicleType = null;
		String name = null;
		double startposx = 0;
		double startposy = 0;
		DoublePoint position = null;
		double direction = 0;
		double speed = 0;
		double fuel = 0;
		int health = 100;

		mistake = null;

		if(Core.vehicleTypeChecker(type) != null) {
			if(Core.userChecker(userID) != null)
				targetUser = Core.userChecker(userID);
			else mistake = ", user not found";
			if(Core.teamChecker(teamID) != null) {
				targetTeam = Core.teamChecker(teamID);
				if(!Core.userTeamFinder(userID).equals(targetTeam))
					mistake = ", user is not in appropiate team";
			} else mistake = ", team not found";


			vehicleType = Core.vehicleTypeChecker(type);

			name = "vehicle_" + Core.vehicles.size() + "(" + type + ")";

			if(Core.isDouble(x) && Core.isDouble(y)) {
				startposx = Core.doubleParser(x, tokens);
				startposy = Core.doubleParser(y, tokens);
				position = new DoublePoint(startposx, startposy);
			} else mistake = ", invalid value for either x or y";

			speed = DudesConfig.SharedVars.getMaxSpeed(vehicleType);
			fuel = DudesConfig.SharedVars.getMaxFuel(vehicleType);
		} else mistake = ", vehicle not found";

		if(mistake == null) {
			targetTeam.addVehicle((VehicleInt) new Vehicle(vehicleType, name, position, 0, direction, speed, fuel, health, true, null));
			for(VehicleInt vehicle : targetTeam.getVehicles()) {
				if(vehicle.getName().equals(name)) {
					targetVehicle = vehicle;
				}
			}
			if(targetVehicle != null)
				targetUser.setUserVehicle(targetVehicle);
		} else {
			System.out.println("Creating vehicle failed" + mistake + ".");
		}
	}

	static void quickAssign(int userID, String vehicleName) {

		mistake = null;

		// if specified user exists, set targetUser
		if(Core.userChecker(userID) != null)
			targetUser = Core.userChecker(userID);
		else mistake = ", user not found";

		// if specified vehicle exists in specified user's team, set targetVehicle
		for(VehicleInt vehicle : Core.userTeamFinder(userID).getVehicles()) {
			if(vehicle.getName().equals(vehicleName)) {
				if(!targetVehicle.getLocked()) {
					targetVehicle = vehicle;
					valid = true;
					break;
				} else mistake = ", vehicle is already locked.";
			} else mistake = ", vehicle not found.";
		}

		// if vehicle was found and wasn't locked, assign vehicle accordingly and lock it.
		if(mistake == null) {
			// if user already had a vehicle, unlock it.
			if(targetUser.getUserVehicle() != null)
				targetUser.getUserVehicle().setLocked(false);
			targetUser.setUserVehicle(targetVehicle);
			targetVehicle.setLocked(true);
		}
		System.out.println("Assigning vehicle failed" + mistake + ".");
	}

	static void clearvehicle(int teamID, int vehicleID) {

		mistake = null;

		if(Core.teamChecker(teamID) != null)
			targetTeam = Core.teamChecker(teamID);
		else mistake = ", team not found";
		if(Core.vehicleChecker(teamID, vehicleID) != null)
			targetVehicle = Core.vehicleChecker(teamID, vehicleID);
		else mistake = ", vehicle not found";

		if(mistake == null) {
			for(Object user : targetTeam.getUsers_Remote()) {
				user = (UserInt) TransparentItemProxy.getItem(user, new Class[] { UserInt.class });
				if(((UserInt) user).getUserVehicle() != null && ((UserInt) user).getUserVehicle().getName().equals(targetVehicle.getName())) {
					((UserInt) user).setUserVehicle(null);
					targetVehicle.setLocked(false);
				}
			}
		} else System.out.println("Clearing vehicle failed" + mistake + ".");
	}

	static void lockVehicle(int teamID, int vehicleID, String input) {

		boolean locked = true;

		mistake = null;

		if(Core.vehicleChecker(teamID, vehicleID) != null)
			targetVehicle = Core.vehicleChecker(teamID, vehicleID);
		else mistake = ", vehicle not found";

		if(input.equals("true"))
			locked = true;
		else if(input.equals("false"))
			locked = false;
		else mistake = ", wrong value for locked/unlocked.";

		if(mistake == null) {
			targetVehicle.setLocked(locked);
			System.out.println(targetVehicle.getName() + "'s lock has been set to " + input + ".");
		} else System.out.println("Locking vehicle failed" + mistake + ".");
	}
}
